#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/shm.h>
#include <stdlib.h>
#include <fcntl.h>

#include "head.h"

void init_list(struct shared_struct *head, int *count, int size) {
    *count = 0;
    struct shared_struct *temp = head;

    int i;
    for (i = 0; i < size; ++i) {
        if (i == size - 1) {
            temp->next = 0;
            return;
        }

        temp->next = temp + 1;
        printf("%p\n %p", temp, temp->next);
        temp = temp->next;
    }
}

int main(int argc, char *argv[]) {
    struct stat fileattr;
    key_t key; /* of type int */
    int shmid; /* shared memory ID */
    void *shmptr;
    struct shared_struct *shared; /* structured shm */
    pid_t childpid1, childpid2;
    char pathname[80], key_str[10], cmd_str[80];
    int shmsize, ret;

    shmsize = TEXT_NUM * sizeof(struct shared_struct) + 3 * sizeof(int);
    printf("max record number = %d, shm size = %d\n", TEXT_NUM, shmsize);

    if (argc < 2) {
        printf("Usage: ./a.out pathname\n");
        return EXIT_FAILURE;
    }
    strcpy(pathname, argv[1]);

    if (stat(pathname, &fileattr) == -1) {
        ret = creat(pathname, O_RDWR);
        if (ret == -1) {
            ERR_EXIT("creat()");
        }
        printf("shared file object created\n");
    }

    key = ftok(pathname, 0x27); /* 0x27 a project ID 0x0001 - 0xffff, 8 least bits used */
    if (key == -1) {
        ERR_EXIT("con: ftok()");
    }
    printf("key generated: IPC key = %x\n", key); /* can set any nonzero key without ftok()*/

    shmid = shmget((key_t)key, shmsize, 0666 | PERM);
    if (shmid == -1) {
        ERR_EXIT("con: shmget()");
    }
    printf("con: shmid = %d\n", shmid);

    shmptr = shmat(shmid, 0, 0); /* returns the virtual base address mapping to the shared memory, *shmaddr=0 decided by kernel */

    if (shmptr == (void *)-1) {
        ERR_EXIT("con: shmat()");
    }
    printf("con: shared Memory attached at %p\n", shmptr);

    int *lock = (int *)shmptr;
    int *count_ptr = (int *)(shmptr + 4);

    struct shared_struct *head = (struct shared_struct *)(shmptr + 8);

    *lock = 0;

    init_list(head, count_ptr, TEXT_NUM);

    printf("\n");

    printList(head, TEXT_NUM);

    sprintf(cmd_str, "ipcs -m | grep '%d'\n", shmid);
    printf("\n------ Shared Memory Segments ------\n");
    system(cmd_str);

    if (shmdt(shmptr) == -1)
    {
        ERR_EXIT("con: shmdt()");
    }

    printf("\n------ Shared Memory Segments ------\n");
    system(cmd_str);

    sprintf(key_str, "%x", key);
    char *argv1[] = {" ", key_str, 0};

    childpid1 = vfork();
    if (childpid1 < 0)
    {
        ERR_EXIT("con: 1st vfork()");
    } else if (childpid1 == 0) {
        execv("./read.o", argv1); /* call shm_read with IPC key */
    } else {
        childpid2 = vfork();
        if (childpid2 < 0){
            ERR_EXIT("con: 2nd vfork()");
        } else if (childpid2 == 0) {
            execv("./write.o", argv1); /* call shmwrite with IPC key */
        } else {
            wait(&childpid1);
            wait(&childpid2);
            /* shmid can be removed by any process knewn the IPC key */
            if (shmctl(shmid, IPC_RMID, 0) == -1) {
                ERR_EXIT("con: shmctl(IPC_RMID)");
            } else {
                printf("con: shmid = %d removed \n", shmid);
                printf("\n------ Shared Memory Segments ------\n");
                system(cmd_str);
                printf("nothing found ...\n");
            }
        }
    }

    exit(EXIT_SUCCESS);
}
